from datetime import timedelta
from typing import Optional
from tortoise.expressions import Q
from fastapi import FastAPI, Depends, UploadFile, File, Query
from pydantic import BaseModel
from starlette.middleware.cors import CORSMiddleware
from starlette.responses import FileResponse
from starlette.staticfiles import StaticFiles
from tortoise.contrib.fastapi import register_tortoise
import os
from tortoise.transactions import in_transaction
import security
from schema import Result

# 确认目录存在，如果不存在则创建
uploads_directory = "uploads"
if not os.path.exists(uploads_directory):
    os.makedirs(uploads_directory)

app = FastAPI()

# 注册Tortoise ORM
register_tortoise(
    app,
    # db_url="mysql://culture:j6SGMTTzE5LYxxrk@123.56.131.61:3306/culture",  # 数据库连接URL
    db_url="mysql://culture:Mb8sMfAMRkrBAGHH@120.55.50.49:3306/culture",  # 数据库连接URL
    modules={"models": ["models"]},  # 模型模块
    generate_schemas=False,  # 是否生成schema
    add_exception_handlers=True,  # 是否添加异常处理程序
)

# 添加CORS中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 允许所有来源
    allow_credentials=True,  # 允许凭证
    allow_methods=["*"],  # 允许所有方法
    allow_headers=["*"],  # 允许所有头
)

# 挂载静态文件目录
app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads")

from models import *
from tortoise.contrib.pydantic import pydantic_model_creator

# 创建User的Pydantic模型
userIn = pydantic_model_creator(User, name="UserIn", exclude_readonly=True)
userOut = pydantic_model_creator(User, name="UserOut", exclude=("password",))


class Login(BaseModel):
    username: str
    password: str


# 登录接口
@app.post("/token", response_model=Result)
async def login(user: Login):
    user = await User.get_or_none(username=user.username, password=user.password)
    if user is None:
        return Result.error("用户名或密码错误")
    expires = timedelta(hours=12)
    data = {"token": security.create_access_token({'sub': user.username}, expires_delta=expires)}
    return Result.ok(data)


# 注册接口
@app.post("/register", response_model=Result)
async def register(payload: userIn):
    user = await User.get_or_none(username=payload.username)
    if user is not None:
        return Result.error("用户名已存在")
    await User.create(**payload.model_dump(exclude_unset=True))
    return Result.ok(None, message="注册成功")


class Profile(BaseModel):
    nickname: Optional[str] = None
    img: Optional[str] = None


# 更新用户信息接口
@app.post("/updata", response_model=Result)
async def updata(payload: Profile, user: User = Depends(security.check_token)):
    await User.filter(id=user.id).update(**payload.model_dump(exclude_unset=True, exclude_none=True))
    return Result.ok(None, message="更新成功")


# 创建Post的Pydantic模型
postIn = pydantic_model_creator(Post, name="PostIn", exclude_readonly=True, exclude=("user_id",))


class UserOut(BaseModel):
    id: int
    username: str
    nickname: str
    img: str

    class Config:
        orm_mode = True
        from_attributes = True


# 获取用户信息接口
@app.get("/getinfo", response_model=Result[UserOut])
async def getinfo(user: User = Depends(security.check_token)):
    user_instance = await User.filter(id=user.id).first()
    if not user_instance:
        return Result.error("查询失败")
    user_data = UserOut.from_orm(user_instance)
    return Result.ok(user_data, message="查询成功")


# 发布帖子接口
@app.post("/posts", response_model=Result)
async def post(payload: postIn, user: User = Depends(security.check_token)):
    await Post.create(title=payload.title, content=payload.content, user_id=user.id, store_name=payload.store_name)
    return Result.ok(None, message="发布成功")


# 点赞帖子接口
@app.post("/posts/like/{post_id}", response_model=Result)
async def like(post_id: int, user: User = Depends(security.check_token)):
    async with in_transaction() as connection:
        post = await Post.get_or_none(id=post_id)
        if post is None:
            return Result.error("帖子不存在")

        like = await Like.get_or_none(post_id=post_id, user_id=user.id)
        if like is None:
            await Like.create(post_id=post_id, user_id=user.id)
            post.like += 1
            if post.store_name is None:
                post.store_name = "无"
            await post.save()
            return Result.ok(None, message="点赞成功")
        else:
            return Result.error("你已经点赞了")


# 收藏帖子接口
@app.post("/posts/favorites/{post_id}")
async def favorite(post_id: int, user: User = Depends(security.check_token)):
    post = await Post.get_or_none(id=post_id)
    if post is None:
        return Result.error("帖子不存在")
    if await Favorite.filter(post_id=post_id, user_id=user.id).exists():
        return Result.error("帖子已收藏")
    await Favorite.create(post_id=post_id, user_id=user.id)
    return Result.ok(None, message="收藏成功")


class PostOut(BaseModel):
    id: int
    title: str
    content: str
    like: int
    user_id: int
    user_img: Optional[str]

    class Config:
        orm_mode = True
        from_attributes = True


# 获取用户收藏的帖子接口
@app.get("/posts/myfavorites", response_model=Result[list[PostOut]])
async def myfavorites(user: User = Depends(security.check_token)):
    favorites = await Favorite.filter(user_id=user.id).values_list('post_id', flat=True)
    posts = await Post.filter(id__in=favorites).prefetch_related('user').all()

    post_outs = []
    for post in posts:
        user_img = post.user.img if post.user else None
        post_outs.append(PostOut(
            id=post.id,
            title=post.title,
            content=post.content,
            like=post.like,
            user_id=post.user_id,
            user_img=user_img
        ))

    return Result.ok(post_outs, message="查询成功")


# 获取用户发布的帖子接口
@app.get("/posts/myposts", response_model=Result[list[PostOut]])
async def myposts1(user: User = Depends(security.check_token)):
    posts = await Post.filter(user_id=user.id).prefetch_related('user')
    post_outs = []
    for post in posts:
        user = post.user
        if user:
            post_outs.append(PostOut(
                id=post.id,
                title=post.title,
                content=post.content,
                like=post.like,
                user_id=user.id,
                user_img=user.img if user else None
            ))
        else:
            post_outs.append(PostOut(
                id=post.id,
                title=post.title,
                content=post.content,
                like=post.like,
                user_id=None,
                user_img=None
            ))

    return Result.ok(post_outs, message="查询成功")


# 搜索帖子接口
@app.get("/posts/search/", response_model=Result[list[PostOut]])
async def search(store_name: Optional[str] = Query(None)):
    if store_name:
        posts = await Post.filter(store_name__icontains=store_name).prefetch_related('user')
    else:
        posts = await Post.all().prefetch_related('user')

    post_outs = []
    for post in posts:
        user = post.user
        if user:
            post_outs.append(PostOut(
                id=post.id,
                title=post.title,
                content=post.content,
                like=post.like,
                user_id=user.id,
                user_img=user.img if user else None
            ))
        else:
            post_outs.append(PostOut(
                id=post.id,
                title=post.title,
                content=post.content,
                like=post.like,
                user_id=None,
                user_img=None
            ))

    return Result.ok(post_outs, message="查询成功")


class StoreOut(BaseModel):
    id: int = None
    index: Optional[str] = None
    num: Optional[str] = None
    name: Optional[str] = None
    sheng: Optional[str] = None
    shi: Optional[str] = None
    store: Optional[str] = None
    img: Optional[str] = None
    location: Optional[str] = None
    contact: Optional[str] = None
    businessHours: Optional[str] = None

    class Config:
        orm_mode = True
        from_attributes = True


# 获取商店信息接口
@app.get("/stores", response_model=Result[list[StoreOut]])
async def getStores(name: Optional[str] = Query(None),
                    store: Optional[str] = Query(None),
                    id: Optional[int] = Query(None),
                    sheng: Optional[str] = Query(None)):
    query = Store.all()
    if id:
        query = query.filter(id=id)
    else:
        if store:
            # query = query.filter(store__icontains=store)+ query.filter(name__icontains=store)
            query = query.filter(Q(name__icontains=store) | Q(store__icontains=store))
        if name:
            query = query.filter(name=name)
        if sheng:
            query = query.filter(sheng=sheng)
    stores = await query
    stores_out = [StoreOut.from_orm(store) for store in stores]
    return Result.ok(data=stores_out, message="查询成功")


class ArtOut(BaseModel):
    id: int = None
    index: Optional[str] = None
    num: Optional[str] = None
    name: Optional[str] = None
    sheng: Optional[str] = None
    shi: Optional[str] = None
    introduce: Optional[str] = None
    type: Optional[str] = None

    class Config:
        orm_mode = True
        from_attributes = True


# 获取艺术品信息接口
@app.get("/arts", response_model=Result[list[ArtOut]])
async def getStores(name: Optional[str] = Query(None),
                    type: Optional[str] = Query(None),
                    id: Optional[int] = Query(None),
                    sheng: Optional[str] = Query(None)):
    query = Art.all()
    if id:
        query = query.filter(id=id)
    else:
        if name:
            query = query.filter(name=name)
        if type:
            query = query.filter(type=type)
        if sheng:
            query = query.filter(sheng=sheng)
    arts = await query
    arts_out = [ArtOut.from_orm(art) for art in arts]
    return Result.ok(data=arts_out, message="查询成功")


# 文件上传接口
@app.post("/file/upload", response_model=Result)
async def upload(file: UploadFile = File(...)):
    filename = file.filename
    with open(f"uploads/{filename}", "wb") as f:
        f.write(await file.read())
    data = {"filename": filename, "path": f"/uploads/{filename}"}
    return Result.ok(data, message="上传成功")


# 文件下载接口
@app.get("/file/download")
async def download(filename: str):
    return FileResponse(f"uploads/{filename}")


if __name__ == '__main__':
    import uvicorn

    uvicorn.run("main:app", reload=True, port=5000)
